import java.time.*;
import java.util.*;
import java.util.regex.Pattern;

public class CinemaSystem {

    public static void main(String[] args) {
        Data.init();
        new MainUI().run();
    }

    private static class Data {
        static final Map<String, User>        users      = new HashMap<>();
        static final Map<Integer, Movie>      movies     = new HashMap<>();
        static final Map<Integer, Hall>       halls      = new HashMap<>();
        static final Map<Integer, Schedule>   schedules  = new HashMap<>();
        static final Map<String, Ticket>      tickets    = new HashMap<>();
        static final Map<Integer, SeatMatrix> seats      = new HashMap<>();

        private static int mid = 1, sid = 1;

        static void init() {           // 5 个影厅
            halls.put(1, new Hall(1, "黄金厅", 8, 10));
            halls.put(2, new Hall(2, "白银厅", 10, 12));
            halls.put(3, new Hall(3, "翡翠厅", 7, 8));
            halls.put(4, new Hall(4, "钻石厅", 7, 8));
            halls.put(5, new Hall(5, "宝石厅", 8, 9));
            users.put("admin", new User("admin", "ynuinfo#777", Role.ADMIN, "00000000000"));
        }

        static int nextMid() { return mid++; }
        static int nextSid() { return sid++; }
    }

    private enum Role { ADMIN, MANAGER, CASHIER }

    private static class User {
        String id, name, pwd, phone; Role role; LocalDateTime create;
        User(String name, String pwd, Role role, String phone) {
            this.id = UUID.randomUUID().toString().substring(0, 6);
            this.name = name; this.pwd = pwd; this.role = role; this.phone = phone;
            this.create = LocalDateTime.now();
        }
    }
    private static class Movie {
        int id; String title, director, actors, intro; int duration; double score;
        Movie(int id, String t, String d, String a, String i, int du, double s) {
            this.id = id; title = t; director = d; actors = a; intro = i; duration = du; score = s;
        }
    }
    private static class Hall { int id, rows, cols; String name;
        Hall(int id, String n, int r, int c) { this.id = id; name = n; rows = r; cols = c; }
    }
    private static class Schedule {
        int id, movieId, hallId; LocalDateTime start; double price;
        Schedule(int id, int m, int h, LocalDateTime st, double p) {
            this.id = id; movieId = m; hallId = h; start = st; price = p;
        }
    }
    private static class SeatMatrix {
        boolean[][] s; int r, c;
        SeatMatrix(int rows, int cols) { r = rows; c = cols; s = new boolean[r][c]; }
        boolean sold(int row, int col) { return s[row-1][col-1]; }
        boolean sell(int row, int col) { if (sold(row, col)) return false; s[row-1][col-1]=true; return true; }
        void refund(int row, int col) { s[row-1][col-1]=false; }
        void print() {
            System.out.print("   ");
            for (int i=1; i<=c; i++) System.out.print(i+" ");
            System.out.println();
            for (int i=1; i<=r; i++) {
                System.out.print(i+"  ");
                for (int j=1; j<=c; j++) System.out.print(sold(i,j)?"X ":"O ");
                System.out.println();
            }
        }
    }
    private static class Ticket {
        String no; int scheduleId; int row, col; String customerPhone;
        Ticket(String no, int sch, int r, int c, String phone) {
            this.no = no; scheduleId = sch; row = r; col = c; customerPhone = phone;
        }
    }

    private static class In {
        private static final Scanner sc = new Scanner(System.in);
        static int     readInt(String tip) { while(true) { System.out.print(tip); try { return Integer.parseInt(sc.nextLine()); } catch (Exception e) { System.out.println("请输入整数"); } } }
        static double  readDouble(String tip) { while(true) { System.out.print(tip); try { return Double.parseDouble(sc.nextLine()); } catch (Exception e) { System.out.println("请输入数字"); } } }
        static String  readLine(String tip) { System.out.print(tip); return sc.nextLine(); }
    }
    private static class Audit { static void log(String op, String action) { System.out.println("[AUDIT] "+op+" -> "+action+"  "+LocalDateTime.now()); } }

    private static class MainUI {
        void run() {
            while (true) {
                System.out.println("\n===== 万东电影院 =====");
                System.out.println("1 登录  2 退出");
                switch (In.readInt("")) {
                    case 1: login(); break;
                    case 2: return;
                }
            }
        }
        private void login() {
            String name = In.readLine("用户名：");
            String pwd  = In.readLine("密码：");
            User u = Data.users.get(name);
            if (u==null || !u.pwd.equals(pwd)) { System.out.println("用户名或密码错误"); return; }
            if ("123456".equals(u.pwd)) {
                u.pwd = In.readLine("首次登录，请修改密码：");
                Audit.log(name, "修改密码");
            }
            switch (u.role) {
                case ADMIN:   new AdminUI(u).run(); break;
                case MANAGER: new ManagerUI(u).run(); break;
                case CASHIER: new CashierUI(u).run(); break;
            }
        }
    }

    private static class AdminUI {
        User u; AdminUI(User u){this.u=u;}
        void run() {
            while (true) {
                System.out.println("\n===== 管理员菜单 =====");
                System.out.println("1 用户管理  2 修改密码  3 退出");
                switch (In.readInt("")) {
                    case 1: userMgmt(); break;
                    case 2: u.pwd = In.readLine("新密码："); Audit.log(u.name,"修改密码"); break;
                    case 3: return;
                }
            }
        }
        private void userMgmt() {
            while (true) {
                System.out.println("--- 用户列表 ---");
                Data.users.values().forEach(v->System.out.println(v.id+" "+v.name+" "+v.role+" "+v.phone+" "+v.create));
                System.out.println("1 添加  2 删除  3 重置密码  4 返回");
                switch (In.readInt("")) {
                    case 1: add(); break;
                    case 2: del(); break;
                    case 3: reset(); break;
                    case 4: return;
                }
            }
        }
        private void add() {
            String name = In.readLine("用户名：");
            if (Data.users.containsKey(name)) { System.out.println("已存在"); return; }
            Role r = Role.valueOf(In.readLine("角色 MANAGER/CASHIER：").toUpperCase());
            String phone = In.readLine("手机号：");
            Data.users.put(name, new User(name,"123456",r,phone));
            Audit.log(u.name,"添加用户 "+name);
        }
        private void del() {
            String name = In.readLine("用户名：");
            if ("admin".equals(name)) { System.out.println("不能删除 admin"); return; }
            if (Data.users.remove(name)==null) System.out.println("不存在");
            else Audit.log(u.name,"删除用户 "+name);
        }
        private void reset() {
            String name = In.readLine("用户名：");
            User t = Data.users.get(name);
            if (t==null) { System.out.println("不存在"); return; }
            t.pwd="123456";
            Audit.log(u.name,"重置密码 "+name);
        }
    }

    private static class ManagerUI {
        User u; ManagerUI(User u){this.u=u;}
        void run() {
            while (true) {
                System.out.println("\n===== 经理菜单 =====");
                System.out.println("1 影片管理  2 排片管理  3 查看座位图  4 修改密码  5 退出");
                switch (In.readInt("")) {
                    case 1: movieMgmt(); break;
                    case 2: scheduleMgmt(); break;
                    case 3: new CashierUI(u).viewSeat(); break; // 直接复用
                    case 4: u.pwd = In.readLine("新密码："); Audit.log(u.name,"修改密码"); break;
                    case 5: return;
                }
            }
        }
        private void movieMgmt() {
            while (true) {
                System.out.println("--- 影片列表 ---");
                Data.movies.values().forEach(System.out::println);
                System.out.println("1 添加  2 修改  3 删除  4 返回");
                switch (In.readInt("")) {
                    case 1: addMovie(); break;
                    case 2: updateMovie(); break;
                    case 3: delMovie(); break;
                    case 4: return;
                }
            }
        }
        private void addMovie() {
            String t = In.readLine("片名：");
            String d = In.readLine("导演：");
            String a = In.readLine("主演：");
            String i = In.readLine("简介：");
            int du  = In.readInt("时长(分钟)：");
            double s = In.readDouble("评分(0-10)：");
            Data.movies.put(Data.nextMid(), new Movie(Data.mid-1,t,d,a,i,du,s));
            Audit.log(u.name,"添加影片 "+t);
        }
        private void updateMovie() {
            int id = In.readInt("影片ID：");
            Movie m = Data.movies.get(id);
            if (m==null) { System.out.println("不存在"); return; }
            m.title    = In.readLine("新片名(回车跳过)："); if ("".equals(m.title)) m.title=m.title;
            m.director = In.readLine("新导演："); if (!"".equals(m.director)) m.director=m.director;
            m.actors   = In.readLine("新主演："); if (!"".equals(m.actors)) m.actors=m.actors;
            m.intro    = In.readLine("新简介："); if (!"".equals(m.intro)) m.intro=m.intro;
            String tmp = In.readLine("新时长(分钟)："); if (!"".equals(tmp)) m.duration=Integer.parseInt(tmp);
            tmp = In.readLine("新评分："); if (!"".equals(tmp)) m.score=Double.parseDouble(tmp);
            Audit.log(u.name,"修改影片 "+m.title);
        }
        private void delMovie() {
            int id = In.readInt("影片ID：");
            if (Data.movies.remove(id)==null) System.out.println("不存在");
            else Audit.log(u.name,"删除影片 "+id);
        }

        private void scheduleMgmt() {
            while (true) {
                System.out.println("--- 排片列表 ---");
                Data.schedules.values().forEach(s->System.out.println(s.id+" | "+Data.movies.get(s.movieId).title+" | "+
                        Data.halls.get(s.hallId).name+" | "+s.start+" | ¥"+s.price));
                System.out.println("1 添加  2 修改  3 删除  4 返回");
                switch (In.readInt("")) {
                    case 1: addSchedule(); break;
                    case 2: updateSchedule(); break;
                    case 3: delSchedule(); break;
                    case 4: return;
                }
            }
        }
        private void addSchedule() {
            int mid = In.readInt("影片ID：");
            int hid = In.readInt("影厅ID(1-5)：");
            System.out.print("日期时间(格式 2025-07-28 19:30)：");
            LocalDateTime dt = LocalDateTime.parse(In.sc.nextLine(), java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
            double price = In.readDouble("票价：");
            Data.schedules.put(Data.nextSid(), new Schedule(Data.sid-1,mid,hid,dt,price));
            Audit.log(u.name,"排片 "+Data.movies.get(mid).title+" 到 "+Data.halls.get(hid).name);
        }
        private void updateSchedule() {
            int id = In.readInt("排片ID：");
            Schedule s = Data.schedules.get(id);
            if (s==null) { System.out.println("不存在"); return; }
            System.out.print("新日期时间(格式同上)：");
            String tmp = In.sc.nextLine();
            if (!"".equals(tmp)) s.start = LocalDateTime.parse(tmp, java.time.format.DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm"));
            tmp = In.readLine("新票价："); if (!"".equals(tmp)) s.price=Double.parseDouble(tmp);
            Audit.log(u.name,"修改排片 "+id);
        }
        private void delSchedule() {
            int id = In.readInt("排片ID：");
            if (Data.schedules.remove(id)==null) System.out.println("不存在");
            else Audit.log(u.name,"删除排片 "+id);
        }
    }

    private static class CashierUI {
        User u; CashierUI(User u){this.u=u;}
        void run() {
            while (true) {
                System.out.println("\n===== 售票员菜单 =====");
                System.out.println("1 查看排片  2 售票  3 退票  4 查看座位图  5 修改密码  6 退出");
                switch (In.readInt("")) {
                    case 1: listSchedule(); break;
                    case 2: sell(); break;
                    case 3: refund(); break;
                    case 4: viewSeat(); break;
                    case 5: u.pwd = In.readLine("新密码："); Audit.log(u.name,"修改密码"); break;
                    case 6: return;
                }
            }
        }
        private void listSchedule() {
            Data.schedules.values().forEach(s->System.out.println(s.id+" | "+Data.movies.get(s.movieId).title+" | "+
                    Data.halls.get(s.hallId).name+" | "+s.start+" | ¥"+s.price));
        }
        private void viewSeat() {
            listSchedule();
            int schId = In.readInt("排片ID：");
            Schedule sch = Data.schedules.get(schId);
            if (sch == null) { System.out.println("排片不存在"); return; }
            SeatMatrix m = Data.seats.computeIfAbsent(schId,
                    k -> new SeatMatrix(Data.halls.get(sch.hallId).rows,
                            Data.halls.get(sch.hallId).cols));
            m.print();
            System.out.println("1 推荐空座位  2 返回");
            switch (In.readInt("")) {
                case 1:
                    boolean found = false;
                    for (int i = 1; i <= m.r; i++)
                        for (int j = 1; j <= m.c; j++)
                            if (!m.sold(i, j)) {
                                System.out.println("推荐空座位：行 " + i + " 列 " + j);
                                found = true;
                            }
                    if (!found) System.out.println("无空座");
                    break;
                case 2:
                    break;
            }
        }
        private void sell() {
            listSchedule();
            int schId = In.readInt("排片ID：");
            Schedule sch = Data.schedules.get(schId);
            if (sch == null) { System.out.println("排片不存在"); return; }

            SeatMatrix m = Data.seats.computeIfAbsent(schId,
                    k -> new SeatMatrix(Data.halls.get(sch.hallId).rows,
                            Data.halls.get(sch.hallId).cols));

            m.print(); // 当前座位
            String seatsStr = In.readLine("选座(如 3-5 3-6)：");
            String phone = In.readLine("购票人电话：");
            String[] arr = seatsStr.split(" ");
            double total = 0;
            List<Ticket> ts = new ArrayList<>();

            for (String a : arr) {
                String[] rc = a.split("-");
                int r = Integer.parseInt(rc[0]), c = Integer.parseInt(rc[1]);
                if (!m.sell(r, c)) { System.out.println(r + "排" + c + "座已售"); continue; }

                String no = UUID.randomUUID().toString().substring(0, 8).toUpperCase();
                Ticket t = new Ticket(no, schId, r, c, phone);
                Data.tickets.put(no, t);
                ts.add(t);
                total += sch.price;
                System.out.println("票号：" + no + "  座位：" + r + "排" + c + "座");
            }
            System.out.println("总价：¥" + total);
            System.out.println("--- 最新座位图 ---");
            m.print();
            Audit.log(u.name, "售票 " + ts.size() + " 张");
        }
        private void refund() {
            String no = In.readLine("票号：");
            Ticket t = Data.tickets.remove(no);
            if (t==null) { System.out.println("票号不存在"); return; }
            SeatMatrix m = Data.seats.get(t.scheduleId);
            m.refund(t.row, t.col);
            System.out.println("退票成功");
            Audit.log(u.name,"退票 "+no);
        }
    }
}